from bodhi.wsgi import HttpResponse
from bodhi.utils.http import patch_vary_headers

CONTENT_TYPES = set([
    "text/plain", "text/html", "text/css", "text/xml",
    "application/x-javascript", "application/javascript", "application/xml", "application/atom+xml",
    "text/javascript", "application/json", "application/xhtml+xml"])

# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
# Content-Length:
# In HTTP, it SHOULD be sent whenever the message's length can be determined 
# prior to being transferred, unless this is prohibited by the rules in sectioni
# 4.4. (ie., non-identity transfer encoding or responses which "MUST NOT" include a message-body (such as the 1xx, 204, and 304 responses and any response to a HEAD request))

# todo: when status code != 200? 
#       if header contains no content-length, then cherrypy server will atomatically return a chunked response.

import io
from gzip import GzipFile

def gzip_compress(s, compresslevel=6):
    with io.BytesIO() as buf,  GzipFile(mode='wb', compresslevel=compresslevel, fileobj=buf) as zfile:
        zfile.write(s)
        zfile.flush() 
        # if not flushed as above , buf.getvalue() will only get partial bytes.
        return buf.getvalue()

class GZipFilter:
    '''performance: zlib.compress(s, level=6) ~5.6ms/100KB in laptop. gzip has similiar performance.
    it's nearly 20 times slower than md5.
    '''
    order = 0

    def __call__(self, view, next):
        next()

        response = view.response
        if response.status_code != 200 or len(response.content) < 200:
            return

        ctype = response.headers.get('Content-Type', '').split(';')[0].lower()
        if ctype not in CONTENT_TYPES:
            return

        # Avoid gzipping if we've already got a content-encoding.
        if response.has_header('Content-Encoding'):
            assert 'accept-encoding' in response.get_header('Vary').lower(), 'Content-Encoding header has been set, however, Vary: Content-Encoding not found.'
            return

        # sets the Vary header, so that caches will base their storage on the Accept-Encoding header.
        patch_vary_headers(response, ('Accept-Encoding',))

        if 'gzip' not in view.request.environ.get('HTTP_ACCEPT_ENCODING', ''):
            return

        # MSIE have issues with gzipped respones of various content types.
        # supposed to be fixed with IE Service Pack #2
        #if "msie" in request.environ.get('HTTP_USER_AGENT', '').lower():
        #    ctype = response.get('Content-Type', '').lower()
        #    if not ctype.startswith("text/") or "javascript" in ctype:
        #        return

        response.content = gzip_compress(response.content)
        response.set_header('Content-Encoding', 'gzip')
        return

